import datetime
import os

import numpy as np
import pandas as pd
import pandas as pds

from app import PASSERBY_UPLOAD_FOLDER
from app.Service.PasserByService import PasserByService
from app.Vendor.ApiException import illegalParameterErr, fileSaveErr, illegalNumberErr
from app.env import TEMP_FILE_WEB_PATH

"""
Chemometrics包内通用的函数，包括读取项目信息，获取存储路径，读取文件内容等
后续可能要进行修改成Service的形式
"""


# 从用户服务中读取项目的信息，以及得到此项目的存储地址
def getProjectInfo():
    userId, projectId, data, label, coordinate = PasserByService().getProjectFileInfo()
    savePath = getSavePath(userId)
    return userId, projectId, data, label, coordinate, savePath


# 根据项目的用户ID和项目ID拼接出用户的存储地址
def getSavePath(userId):
    userId, projectId, data, label, coordinate = PasserByService().getProjectFileInfo()
    path = os.getcwd() + PASSERBY_UPLOAD_FOLDER + userId + "/" + projectId + "/"
    if not os.path.exists(path):
        # 如果不存在则创建目录
        # 创建目录操作函数
        os.makedirs(path)
    return path


# 根据env中的网络位置拼接出文件的url，thread_path没啥用，但是改了有点麻烦先不改了
def getWebPath(thread_path):
    userId, projectId = PasserByService().getUserInfo()
    path = TEMP_FILE_WEB_PATH + userId + '/' + projectId + '/'
    return path


# 拼接出生成图片的url，shortPath其实就是预设的图片名字加上当前日期的文件名
def getWebImagePath(thread_path, shortPath):
    webImagePath = getWebPath(thread_path) + shortPath
    return webImagePath


# 拼接出生成图片的实际地址
def getImagePath(thread_path, imageFilename):
    imagePath = thread_path + imageFilename + "_" + datetime.datetime.now().strftime('%Y%m%d%H%M%S') + ".png"
    shortPath = imageFilename + "_" + datetime.datetime.now().strftime('%Y%m%d%H%M%S') + ".png"
    return imagePath, shortPath


# 读取csv格式或者excel的文件
def readCsvOrExcel(file):
    file_name, type = os.path.splitext(file)
    if type == ".csv":
        data = pds.read_csv(file, delimiter=',', dtype=None, header=None)
    elif type == ".xlsx" or type == ".xls":
        data = pds.read_excel(file, sheet_name=0)
    else:
        raise fileSaveErr()
    if (data.shape[0] == 0 or data.shape[1] == 0):
        raise fileSaveErr(400, "不能上传空文件")
    return data


# 读取csv格式或者excel的文件的信息
def readCsvOrExcelInfo(file):
    file_name, type = os.path.splitext(file)
    if type == ".csv":
        try:
            data = np.array(pds.read_csv(file, delimiter=',', dtype=None, header=None))
        except:
            raise illegalNumberErr()
    elif type == ".xlsx" or type == ".xls":
        try:
            data = np.array(pds.read_excel(file, sheet_name=0))
        except:
            raise illegalNumberErr()
    else:
        raise fileSaveErr()
    if (data.shape[0] == 0 or data.shape[1] == 0):
        raise fileSaveErr(400, "不能上传空文件")
    return data.shape[0], data.shape[1]


# 读取项目的3个主要文件，并返回成np数组的形式
def readData(xfile, yfile, ppmfile, outliers_index=None):
    x_data = np.array(readCsvOrExcel(xfile))
    y_data = readCsvOrExcel(yfile)
    ppm_data = np.array(readCsvOrExcel(ppmfile))

    y_rows, y_cols = y_data.shape[0], y_data.shape[1]

    for i in range(y_cols):
        y_data_single_col = y_data.iloc[:, i]
        y_data_single_col = pds.Categorical(y_data_single_col).codes
        y_data.iloc[:, i] = y_data_single_col

    y_data = np.array(y_data)
    x_data, y_data = deleteOutliers(x_data, y_data, outliers_index)
    return x_data, y_data, ppm_data


# PowerAnalysis方法中，根据类别的信息获取样本，不支持显示离群点
def readDataForPA(xfile, yfile, ppmfile, ycol, t1, outliers_index=None):
    x_data = np.array(readCsvOrExcel(xfile))
    y_data = np.array(readCsvOrExcel(yfile))
    ppm_data = np.array(readCsvOrExcel(ppmfile))
    # 根据outliers_index删除部分点
    x_data, y_data = deleteOutliers(x_data, y_data, outliers_index)
    # 转换回pd.dataframe进行数据处理
    x_data = pd.DataFrame(x_data)
    y_data = pd.DataFrame(y_data)

    y_data[ycol] = y_data[ycol].astype(str)

    x_data = x_data.loc[y_data[ycol] == t1]
    y_data = y_data.loc[y_data[ycol] == t1]

    if (x_data.size == 0):
        raise Exception("所选类别不存在")

    y_rows, y_cols = y_data.shape[0], y_data.shape[1]

    for i in range(y_cols):
        y_data_single_col = y_data.iloc[:, i]

        y_data_single_col = pds.Categorical(y_data_single_col).codes
        y_data.iloc[:, i] = y_data_single_col

    x_data = np.array(x_data)
    y_data = np.array(y_data)
    return x_data, y_data, ppm_data


# Plsda方法中，根据类别的信息获取样本，不支持显示离群点
def readDataForPLSDA(xfile, yfile, ppmfile, ycol, t1, t2, outliers_index=None):
    x_data = np.array(readCsvOrExcel(xfile))
    y_data = np.array(readCsvOrExcel(yfile))
    ppm_data = np.array(readCsvOrExcel(ppmfile))
    # 根据outliers_index删除部分点
    x_data, y_data = deleteOutliers(x_data, y_data, outliers_index)
    # 转换回pd.dataframe进行数据处理
    x_data = pd.DataFrame(x_data)
    y_data = pd.DataFrame(y_data)

    y_data[ycol] = y_data[ycol].astype(str)

    x_data_t1 = x_data.loc[y_data[ycol] == t1]
    y_data_t1 = y_data.loc[y_data[ycol] == t1]
    x_data_t2 = x_data.loc[y_data[ycol] == t2]
    y_data_t2 = y_data.loc[y_data[ycol] == t2]

    if (x_data_t1.size == 0 or x_data_t2.size == 0):
        raise illegalParameterErr()

    x_data = pd.concat([x_data_t1, x_data_t2])
    y_data = pd.concat([y_data_t1, y_data_t2])

    y_rows, y_cols = y_data.shape[0], y_data.shape[1]

    for i in range(y_cols):
        y_data_single_col = y_data.iloc[:, i]

        y_data_single_col = pds.Categorical(y_data_single_col).codes
        y_data.iloc[:, i] = y_data_single_col

    x_data = np.array(x_data)
    y_data = np.array(y_data)
    return x_data, y_data, ppm_data


# Plsr方法中，根据类别的信息获取样本，不支持显示离群点
def readDataForPLSR(xfile, yfile, ppmfile, ycol, outliers_index=None):
    x_data = np.array(readCsvOrExcel(xfile))
    y_data = np.array(readCsvOrExcel(yfile))
    ppm_data = np.array(readCsvOrExcel(ppmfile))
    # 根据outliers_index删除部分点
    x_data, y_data = deleteOutliers(x_data, y_data, outliers_index)
    # 转换回pd.dataframe进行数据处理
    x_data = pd.DataFrame(x_data)
    y_data = pd.DataFrame(y_data)

    y_rows, y_cols = y_data.shape[0], y_data.shape[1]

    for i in range(y_cols):
        y_data_single_col = y_data.iloc[:, i]

        y_data_single_col = pds.Categorical(y_data_single_col).codes
        y_data.iloc[:, i] = y_data_single_col

    x_data = np.array(x_data)
    y_data = np.array(y_data)
    return x_data, y_data, ppm_data


# PA方法中，根据类别的信息获取样本，不支持显示离群点
def readDataForPA(xfile, yfile, ppmfile, ycol, t1, outliers_index=None):
    x_data = np.array(readCsvOrExcel(xfile))
    y_data = np.array(readCsvOrExcel(yfile))
    ppm_data = np.array(readCsvOrExcel(ppmfile))
    # 根据outliers_index删除部分点
    x_data, y_data = deleteOutliers(x_data, y_data, outliers_index)
    # 转换回pd.dataframe进行数据处理
    x_data = pd.DataFrame(x_data)
    y_data = pd.DataFrame(y_data)

    y_data[ycol] = y_data[ycol].astype(str)

    x_data_t1 = x_data.loc[y_data[ycol] == t1]
    y_data_t1 = y_data.loc[y_data[ycol] == t1]

    if (x_data_t1.size == 0):
        raise illegalParameterErr()

    x_data = pd.concat([x_data_t1])
    y_data = pd.concat([y_data_t1])

    y_rows, y_cols = y_data.shape[0], y_data.shape[1]

    for i in range(y_cols):
        y_data_single_col = y_data.iloc[:, i]

        y_data_single_col = pds.Categorical(y_data_single_col).codes
        y_data.iloc[:, i] = y_data_single_col

    x_data = np.array(x_data)
    y_data = np.array(y_data)
    return x_data, y_data, ppm_data


# 根据输入表单中的离群点的索引进行删除
def deleteOutliers(x, y, outliers_index):
    if outliers_index is not None:
        x = np.delete(x, outliers_index, axis=0)
        y = np.delete(y, outliers_index, axis=0)
    return x, y
